package com.damocode.starter.httpclient.domain;

import com.damocode.starter.httpclient.autoconfigure.HttpClientPoolProperties;
import com.damocode.starter.httpclient.helper.FormatType;
import com.damocode.starter.httpclient.helper.MethodType;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ContentType;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public abstract class HttpRequest {

    private String url;
    private MethodType method = null;
    private Map<String, String> headers = new HashMap();
    private final Map<String, String> urlParameters = new HashMap();
    protected FormatType contentType = null;
    protected String encoding = "UTF-8";
    protected Integer connectTimeout = null;
    protected Integer socketTimeout = null;

    public HttpRequest(String url) {
        this(url,MethodType.GET);
    }

    public HttpRequest(String url,MethodType method){
        this.url = url;
        this.method = method;
    }

    public HttpRequest(String url, Map<String, String> headers) {
        this(url);
        if (null != headers) {
            this.headers = headers;
        }
    }

    public HttpRequest(String url,MethodType method, Map<String, String> headers) {
        this(url,method);
        if (null != headers) {
            this.headers = headers;
        }
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public MethodType getMethod() {
        return method;
    }

    public void setMethod(MethodType method) {
        this.method = method;
        if (MethodType.PUT == method) {
            this.setContentType(FormatType.JSON);
        }
    }

    public Integer getConnectTimeout() {
        return connectTimeout;
    }

    public void setConnectTimeout(Integer connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public Integer getSocketTimeout() {
        return socketTimeout;
    }

    public void setSocketTimeout(Integer socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    public void putHeaderParameter(String name, String value) {
        if (null != name && null != value) {
            this.headers.put(name, value);
        }
    }

    public String getHeaderValue(String name) {
        return this.headers.get(name);
    }

    public Map<String, String> getHeaders() {
        return Collections.unmodifiableMap(this.headers);
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public ContentType getContentTypeValue() {
        if (null != getContentType() && null != getEncoding()) {
            return ContentType.create(getContentType().name(),getEncoding());
        }
        return null != getContentType() ? ContentType.create(getContentType().name()): null;
    }

    public FormatType getContentType() {
        return contentType;
    }

    public void setContentType(FormatType contentType) {
        this.contentType = contentType;
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    protected void setParameter(Map<String, String> map, String name, Object value) {
        if (null != map && null != name && null != value) {
            map.put(name, String.valueOf(value));
        }
    }

    public Map<String, String> getUrlParameters() {
        return Collections.unmodifiableMap(this.urlParameters);
    }

    public <K> void putUrlParameter(String name, K value) {
        this.setParameter(this.urlParameters, name, value);
    }


    protected void putUrlParameter(String name, String value) {
        this.setParameter(this.urlParameters, name, value);
    }

    public String getQueryString() {
        if (getUrlParameters().isEmpty()) {
            return "";
        }
        StringBuilder queryStr = new StringBuilder();
        getUrlParameters().forEach((key, value) -> {
            try {
                queryStr.append("&").append(key)
                        .append("=")
                        .append(null == value ? "" : URLEncoder.encode(value, encoding));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        });
        return queryStr.toString().substring(1);
    }

    public String getUrlWithQueryString() {
        String apiUrl = url;
        String queryStr = getQueryString();
        if (!StringUtils.isEmpty(queryStr)) {
            apiUrl = apiUrl + (apiUrl.contains("?") ? "&" : "?") + queryStr;
        }
        return apiUrl;
    }

    public HttpUriRequest parseToHttpRequest(HttpClientPoolProperties httpClientPoolProperties) {
        RequestBuilder builder = RequestBuilder.create(this.getMethod().name());
        builder.setUri(getUrlWithQueryString());
        getHeaders().forEach((k,v) -> builder.addHeader(k,v));
        this.buildRequestBuilder(httpClientPoolProperties, builder);
        int connectTimeout = this.getConnectTimeout() != null ? this.getConnectTimeout() : httpClientPoolProperties.getConnectionTimeoutMillis();
        int socketTimeout = this.getSocketTimeout() != null ? this.getSocketTimeout() : httpClientPoolProperties.getSocketTimeout();
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).setConnectionRequestTimeout(httpClientPoolProperties.getConnectionRequestTimeout()).build();
        builder.setConfig(requestConfig);
        return builder.build();
    }

    protected abstract void buildRequestBuilder(HttpClientPoolProperties httpClientPoolProperties, RequestBuilder builder);

}
